package org.yagnus.scala.derivatives;
/**
 * This class implements an iterator wrapper that allows us to break out of a for comprehension. The semantics
 * is this:
 * 
 * <code>
 * for{
 * 	   x<-makeBreakable(some pre existing iterator)
 *     if x.breakAfter{ x.value >= 4}
 *    }{
 * 	   do stuff with x;
 * 	   println(x);
 * }
 * </code>
 * 
 * This code will loop through that pre-existing iterator as much as possible but will stop after the condition inside
 * breakAfter has become true. (we can't really do breakWhen because when we know the current value of x, we'd have done
 * an iterator.next on the underlying iterator and can't push it back in.) There are two other ways to express this:
 * 
 *    if x.breakAfter{ x.value >= 4}
 * 
 * The frowning face operator:
 * 
 *    if { x.value >= 4} >-: x
 *  
 *  and the bleeped-for-letter-word-operator:
 *  
 *    if { x.value >= 4} *&^: x
 *  
 * @author hsb
 *
 */
final object BreakableIterator {

    trait BreakableIteratedValue[A] {
        def get : A;
        final def value = get;
        final def apply = get;
        def break : Unit;

        final def breakAfter(f : ⇒ Boolean) : Boolean = {
            if (f) break;
            true; //never break current
        }

        final def breakAfter(f : A ⇒ Boolean) : Boolean = {
            if (f(get)) break;
            true; //never break current
        }

        final def stopAfter(f : ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * Frown break
         **/
        final def >-:(f : ⇒ Boolean) : Boolean = breakAfter(f);
        
        /**
         * Frown break
         **/
        final def >-:(f : A ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * bleeped 4-lettered curse break
         **/
        final def *&^:(f : ⇒ Boolean) : Boolean = breakAfter(f);

        /**
         * bleeped 4-lettered curse break
         **/
        final def *&^:(f : A ⇒ Boolean) : Boolean = breakAfter(f);

        override def toString : String = get.toString;
        
    }

    final class BreakableIterator[A](itr : Iterator[A]) extends Iterator[BreakableIteratedValue[A]] {
        protected var breakOut = false;
        class MyBreakableIteratedValue(a : A) extends BreakableIteratedValue[A] {
            override def get = a;
            override def break { breakOut = true };
        }
        final def broken = breakOut;
        final def hasNext : Boolean = !breakOut && itr.hasNext;
        final def next : BreakableIteratedValue[A] = new MyBreakableIteratedValue(itr.next);
        final def breakable = this;
    }

    implicit final def makeBreakable[A](in : Iterator[A]) = new BreakableIterator(in);
    implicit final def makeBreakable[A](in : Iterable[A]) = new BreakableIterator(in.iterator);

    implicit final def recoverOriginal[A](l : BreakableIteratedValue[A]) : A = l.get;
}
